//
//  TGMBaseModel.m
//  TravelGuideMdd
//
//  Created by Song Xiaofeng on 13-8-6.
//  Copyright (c) 2013年 mafengwo.com. All rights reserved.
//

#import "TGMBaseModel.h"
#import "CacheDB.h"
#import "BaseItem.h"

@implementation TGMBaseModelActionData

+ (instancetype)dataWithApi:(NSString *)aApiUrl
                 httpMethod:(RequestHttpMethod)httpMethod
                     params:(NSDictionary *)aParams
                     dataId:(NSString *(^)(NSString *))dataIdBlock
{
    TGMBaseModelActionData *data = [[TGMBaseModelActionData alloc] init];
    data->_apiUrl = [aApiUrl copy];
    data->_params = [NSDictionary dictionaryWithDictionary:aParams];
    data.httpMethod = httpMethod;
    
    // dataId
    MFWJson* json =  [MFWJson jsonWithObject:aParams];
    NSString* dataId = [json stringJson];
    dataId = dataId.length ? dataId : mfwDefaultDataId;
    if (dataIdBlock)
    {
        dataId = dataIdBlock(dataId);
    }
    
    data->_dataId = [dataId copy];
    //DLog(@"_dataId is -----> %@",data->_dataId);
    return data;
}

+ (instancetype)dataWithApi:(NSString *)aApiUrl
                     params:(NSDictionary *)aParams
                     dataId:(NSString *(^)(NSString *))dataIdBlock
{
    return [self dataWithApi:aApiUrl httpMethod:kRequestHttpMethodPost params:aParams dataId:dataIdBlock];
}

+ (instancetype)dataWithApi:(NSString *)aApiUrl params:(NSDictionary *)aParams
{
    return [self dataWithApi:aApiUrl params:aParams dataId:nil];
}

+ (instancetype)dataWithApi:(NSString *)aApiUrl httpMethod:(RequestHttpMethod)httpMethod params:(NSDictionary *)aParams
{
    return [self dataWithApi:aApiUrl httpMethod:httpMethod params:aParams dataId:nil];
}

@end

@interface MFWServiceModel()
- (BOOL)insertAheadListOriginItem:(id)item forDataId:(NSString*)dataId;
@end

@implementation TGMBaseModel

- (id)init
{
    self = [super init];
    if (self)
    {
        _parsedModelItemDataDic = [NSMutableDictionary dictionaryWithCapacity:7];
    }
    return self;
}
- (MFWTask*)requestRefresh:(TGMBaseModelActionData *)data
{
    NSAssert(NO, @"subclass %@ should implement method requestRefresh: ", self);
    return nil;
}
- (MFWTask*)requestGetMore:(TGMBaseModelActionData *)data
{
    NSAssert(NO, @"subclass %@ should implement method requestGetMore: ", self);
    return nil;
}

- (Class)classOfModelItemForDataId:(NSString*)dataId;
{
    return [BaseItem class];
}

- (NSString*)keyOfPersistentCacheForDataId:(NSString*)dataId;
{
    return nil;
}

- (NSMutableArray*)_listModelItemForDataId:(NSString*)dataId;
{
    NSMutableArray *_parsedModelItemList = [_parsedModelItemDataDic valueForKey:dataId];
    if ([_parsedModelItemList count] == 0 )
    {
        if ([self bSupportPersistentCache:dataId])
        {
            [self jsonDataForDataId:dataId];//try to trigger load cache
            
            _parsedModelItemList = [_parsedModelItemDataDic valueForKey:dataId];
        }
        
        if ([_parsedModelItemList count] == 0 )
        {
            _parsedModelItemList = [NSMutableArray array];
            [_parsedModelItemDataDic setValue:_parsedModelItemList forKey:dataId];
        }
        
    }
    
    return _parsedModelItemList;
}

- (NSArray*)listModelItemForDataId:(NSString*)dataId;
{
    return [[self _listModelItemForDataId:dataId] copy];
}

//- (NSArray*)listModelItem;
//{
//    return [self listModelItemForDataId:mfwDefaultDataId];
//}

#pragma mark - Modify list item
- (BOOL)appendModelItem:(BaseItem*)item forDataId:(NSString*)dataId;
{
    BOOL res = NO;
    if (item)
    {
        NSMutableArray *listArray = [self _listModelItemForDataId:dataId];
        if ([listArray isKindOfClass:[NSMutableArray class]])
        {
            [listArray addObject:item];
            res = [self appendListItem:item.originDict forDataId:dataId];
        }
        
    }
    
    if (res)
    {
        [self persistentCurrentDataToCacheForTaskId:dataId];
    }
    
    return res;
}

- (BOOL)insertAheadModelItem:(BaseItem*)item forDataId:(NSString*)dataId;
{
    BOOL res = NO;
    if (item)
    {
        NSMutableArray *listArray = [self _listModelItemForDataId:dataId];
        if ([listArray isKindOfClass:[NSMutableArray class]])
        {
            [listArray insertObject:item atIndex:0];
            res = [self insertAheadListOriginItem:item.originDict forDataId:dataId];
        }
    }
    
    if (res)
    {
            [self persistentCurrentDataToCacheForTaskId:dataId];
    }
    
    return res;
}

- (void)removeListItemAntIndexSet:(NSMutableIndexSet *)indexSet forDataId:(NSString *)dataId
{
    NSMutableArray *listArray = [self _listModelItemForDataId:dataId];
    
    if ([listArray isKindOfClass:[NSMutableArray class]])
    {
        [listArray removeObjectsAtIndexes:indexSet];
        [super removeListItemAntIndexSet:indexSet forDataId:dataId];
        
        [self persistentCurrentDataToCacheForTaskId:dataId];
    }
}
- (void)removeListItemForDataId:(NSString*)dataId
{
    NSMutableArray *listArray = [self _listModelItemForDataId:dataId];
    
    if ([listArray isKindOfClass:[NSMutableArray class]])
    {
        [listArray removeAllObjects];
        [super removeListItemForDataId:dataId];
        
        [self removePersistentCurrentDataToCacheForTaskId:dataId];
    }
}

- (NSUInteger)deleteItemForDataId:(NSString *)dataId usingPassingTest:(BOOL(^)(id obj, NSUInteger idx, BOOL *stop))predicate;
{
    NSUInteger count = 0;
    if (predicate)
    {
        NSMutableArray *listArray = [_parsedModelItemDataDic valueForKey:dataId];
        if ([listArray isKindOfClass:[NSMutableArray class]])
        {
            NSMutableIndexSet *toDelIndexSet = [NSMutableIndexSet indexSet];
            [listArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                if (predicate(obj, idx, stop))
                {
                    [toDelIndexSet addIndex:idx];
                }
            }];
            
            count = [toDelIndexSet count];
            
            if (count > 0)
            {
                [self removeListItemAntIndexSet:toDelIndexSet forDataId:dataId];
            }
        }
    }
    
    return count;
}

/**
 将当前的list（可能被修改过）缓存起来
 */
- (void)persistentCurrentDataToCacheForTaskId:(NSString*)dataId
{
    if ([self bSupportPersistentCache:dataId])
    {
        NSString *cacheKey = [self keyOfPersistentCacheForDataId:dataId];
        NSAssert(![MFWStringUtils isEmptyString:cacheKey], @"bSupportPersistentCache is YES, but KEY OF CACHE is empty!!");
        
        MFWJson *jsonObj = [self jsonDataForDataId:dataId];
        NSString *jsonString = [jsonObj stringJson];
        
        dispatch_async(dispatch_get_main_queue(),
                       ^{
                           [CacheDB setValue:jsonString forKey:cacheKey];
                       });
    }
}

- (void)removePersistentCurrentDataToCacheForTaskId:(NSString*)dataId
{
    NSString *cacheKey = [self keyOfPersistentCacheForDataId:dataId];
    dispatch_async(dispatch_get_main_queue(),
                   ^{
                       [CacheDB setValue:nil forKey:cacheKey];
                   });
}

#pragma mark -

- (NSUInteger)countOfReceivedListDataForDataId:(NSString*)dataId;
{
    return [[_parsedModelItemDataDic valueForKey:dataId] count];
}

- (BOOL)processServiceSuccRawData:(NSData*)rawData parsedJson:(MFWJson*)jsonObj forTask:(MFWHttpTask*)task
{
    [super processServiceSuccRawData:rawData parsedJson:jsonObj forTask:task];
    
    NSString *dataId = task.dataIdentifier.dataId;
    NSString *listKey = [self keyOfListData];
    
    //解析并处理列表型数据
    if ([MFWStringUtils isEmptyString:listKey])
    {//非列表型数据
        // do nothing
    }
    else if ([jsonObj hasValueForKey:listKey])
    {//列表型数据
        
        NSArray *parsedModelItemArray = [self parseModelItemArrayFromJsonArray:[jsonObj jsonForKey:listKey]];
        
        task.itemArrayData = parsedModelItemArray;
        
        if (task.dataRequestType == kMFWDataRequest_Refresh)
        {// refresh
            [_parsedModelItemDataDic setValue:parsedModelItemArray forKey:dataId];
        }
        else if (task.dataRequestType == kMFWDataREquest_GetMore)
        {// get more
            //合并list数据
            NSMutableArray *listArray = [_parsedModelItemDataDic valueForKey:dataId];
            if ([listArray isKindOfClass:[NSMutableArray class]])
            {
                [listArray addObjectsFromArray:parsedModelItemArray];
            }
        }
    }
    return YES;
}

- (NSArray*)parseModelItemArrayFromJsonArray:(MFWJson*)jsonArray
{
    return [TGMBaseModel parseModelItemArrayFromJsonArray:jsonArray itamClass:[self classOfModelItemForDataId:0]];
}

+ (NSArray*)parseModelItemArrayFromJsonArray:(MFWJson*)jsonArray itamClass:(Class)clazz;
{
    if ([jsonArray.jsonObj isKindOfClass:[NSArray class]])
    {
        if (clazz == nil)
        {
            return [jsonArray.jsonObj copy];
        }
        else
        {
            NSUInteger count = [jsonArray count];
            NSMutableArray *modelItemArray = [NSMutableArray arrayWithCapacity:count];
            
            for (NSUInteger i = 0; i < count; i++)
            {
                MFWJson *jsonItem = [jsonArray jsonAtIndex:i];
                id modelItem = [[clazz alloc] initWithJson:jsonItem];
                [modelItemArray addObject:modelItem];
            }
            return modelItemArray;
        }
    }
    return nil;
}

- (BOOL)needRefreshForDataId:(NSString*)dataId;
{
    MFWDataStatus *dataStatus = [self dataStatusForDataId:dataId];
    
    if (dataStatus.statusType == kDataStatus_EmptyInital)
    {
        return YES;
    }
    else if (dataStatus.statusType == kDataStatus_CacheData)
    {
        NSDate *launchDate = self.launchDateStamp;
        NSTimeInterval interval = [dataStatus.refreshDate timeIntervalSinceDate:launchDate];
        if ((int)interval < 0)
        {
            return YES;
        }
    }
    else if ([self countOfReceivedListDataForDataId:dataId] <= 0)
    {
        return YES;
    }
    
    return NO;
}

- (NSString*)optHasMoreKeyForDataId:(NSString*)dataId
{
    return @"has_more";
}

- (BOOL)canGetMoreForDataId:(NSString*)dataId;
{
    MFWDataStatus *dataStatus = [self dataStatusForDataId:dataId];
    
    MFWJson *jsonData = [self jsonDataForDataId:dataId];
    NSString *kHasmore = [self optHasMoreKeyForDataId:dataId];
    
    if (
        (dataStatus.statusType == kDataStatus_SuccRefresh
         || dataStatus.statusType == kDataStatus_ErrorRefresh
         || dataStatus.statusType == kDataStatus_SuccGetmore
         || dataStatus.statusType == kDataStatus_ErrorGetmore
         || dataStatus.statusType == kDataStatus_MoreGeting))
    {
        if ([jsonData hasValueForKey:kHasmore])
        {
            BOOL bHasMore= ([jsonData intValueForKey:kHasmore] == 1);
            return bHasMore;
        }
        else
        {
            return YES;
        }
    }
    
    return NO;
}

- (NSTimeInterval)maxCacheAge;
{
    return -1;
}

#pragma mark - internal package functions
/**
 *	deal with data-caching, default only cache REFRESH operation's raw data.
 *  This function called after @selector(processSuccRawData:parsedJson:forTask:).
 *  Sub-class could override it to implements its' own service appeal.
 *
 *	@param	rawData
 *	@param	jsonObj json data of key "data"
 *	@param	task
 */
- (void)_processPersistentCacheRawData:(NSData*)rawData parsedJson:(MFWJson*)jsonObj forTask:(MFWHttpTask*)task
{
    if (task.dataRequestType == kMFWDataRequest_Refresh)
    {
        NSString *cacheKey = [self keyOfPersistentCacheForDataId:task.dataIdentifier.dataId];
        NSAssert(![MFWStringUtils isEmptyString:cacheKey], @"bSupportPersistentCache is YES, but KEY OF CACHE is empty!!");
        
        NSString *jsonString = [jsonObj stringJson];
        
        dispatch_async(dispatch_get_main_queue(),
                       ^{
                           [CacheDB setValue:jsonString forKey:cacheKey];
                       });
        
    }
}

- (void)synchronizeCacheForDataId:(NSString*)dataId;
{
    NSString *cacheKey = [self keyOfPersistentCacheForDataId:dataId];
    NSAssert(![MFWStringUtils isEmptyString:cacheKey], @"bSupportPersistentCache is YES, but KEY OF CACHE is empty!!");
    
    MFWJson *jsonObj = [self jsonDataForDataId:dataId];
    
    NSString *jsonString = [jsonObj stringJson];
    
    dispatch_async(dispatch_get_main_queue(),
                   ^{
                       [CacheDB setValue:jsonString forKey:cacheKey];
                   });
}

- (MFWJson*)_loadCacheDataForDataId:(NSString*)dataId cacehDate:(NSDate**)pDate
{
    NSString *cacheKey = [self keyOfPersistentCacheForDataId:dataId];
    NSAssert(![MFWStringUtils isEmptyString:cacheKey], @"bSupportPersistentCache is YES, but KEY OF CACHE is empty!!");
    
    CacheItem *cacheItem = [CacheDB cacheItemForKey:cacheKey];
    
    NSString *cacheValue = cacheItem.value;

    //检查缓存是否过期
    NSTimeInterval maxCacheInterval = [self maxCacheAge];
    if (maxCacheInterval > 0 && cacheItem.utime)
    {
        if ([cacheItem.utime timeIntervalSinceNow] < -maxCacheInterval)
        {
            [CacheDB setValue:nil forKey:cacheKey];
            return nil;
        }
    }
    
    if (cacheItem.utime)
    {
        *pDate = cacheItem.utime;
    }
    
    if (!cacheValue)
    {
        return nil;
    }
    
    return [MFWJson jsonWithString:cacheValue];
}

- (BOOL)processCacheData:(MFWJson*)cacheData forDataId:(NSString*)dataId;
{
    BOOL res = [super processCacheData:cacheData forDataId:dataId];
    
    NSString *listKey = [self keyOfListData];
    if (![MFWStringUtils isEmptyString:listKey] && res)
    {
        NSArray *parsedModelItemArray = [self parseModelItemArrayFromJsonArray:[cacheData jsonForKey:listKey]];
        [_parsedModelItemDataDic setValue:parsedModelItemArray forKey:dataId];
    }

    return res;
}

@end

@implementation MFWHttpTask(TGMBaseModelHelper)
- (NSArray*)relativeListModelItemForDataId;
{
    TGMBaseModel *model = [self relativeDataModel];
    
    if ([model isKindOfClass:[TGMBaseModel class]])
    {
        return [model listModelItemForDataId:self.dataIdentifier.dataId];
    }

    return nil;
}

- (BOOL)relativeModelNeedRefresh;
{
    TGMBaseModel *model = [self relativeDataModel];
    
    if ([model isKindOfClass:[TGMBaseModel class]])
    {
        return [model needRefreshForDataId:self.dataIdentifier.dataId];
    }
    
    return NO;
}

- (BOOL)relativeModelCanGetMore;
{
    TGMBaseModel *model = [self relativeDataModel];
    
    if ([model isKindOfClass:[TGMBaseModel class]])
    {
        return [model canGetMoreForDataId:self.dataIdentifier.dataId];
    }
    
    return NO;
}
@end


static NSDate *launchDateStamp = nil;
@implementation TGMBaseModel(launchDateStamp)

- (NSDate*)launchDateStamp
{
    return launchDateStamp;
}

+ (NSDate*)launchDateStamp
{
    return launchDateStamp;
}

+ (void)handleLaunchDateStamp
{
    launchDateStamp = [NSDate date];
}

__attribute__((constructor)) static void addInitLauchingTimeStamp()
{
    [TGMBaseModel handleLaunchDateStamp];
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:[TGMBaseModel class]
               selector:@selector(handleLaunchDateStamp)
                   name:UIApplicationDidFinishLaunchingNotification
                 object:nil];
    
    [center addObserver:[TGMBaseModel class]
               selector:@selector(handleLaunchDateStamp)
                   name:UIApplicationDidBecomeActiveNotification
                 object:nil];
}

@end







